//
// ********************************************************************
// * License and Disclaimer                                           *
// *                                                                  *
// * The  Geant4 software  is  copyright of the Copyright Holders  of *
// * the Geant4 Collaboration.  It is provided  under  the terms  and *
// * conditions of the Geant4 Software License,  included in the file *
// * LICENSE and available at  http://cern.ch/geant4/license .  These *
// * include a list of copyright holders.                             *
// *                                                                  *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work  make  any representation or  warranty, express or implied, *
// * regarding  this  software system or assume any liability for its *
// * use.  Please see the license in the file  LICENSE  and URL above *
// * for the full disclaimer and the limitation of liability.         *
// *                                                                  *
// * This  code  implementation is the result of  the  scientific and *
// * technical work of the GEANT4 collaboration.                      *
// * By using,  copying,  modifying or  distributing the software (or *
// * any work based  on the software)  you  agree  to acknowledge its *
// * use  in  resulting  scientific  publications,  and indicate your *
// * acceptance of all terms of the Geant4 Software license.          *
// ********************************************************************

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

#include "RunAction.hh"

#include <fmt/format.h>

#include <chrono>
#include <stdexcept>

#include "G4PSEnergyDeposit.hh"
#include "G4RunManager.hh"
#include "G4ScoringManager.hh"
#include "G4SystemOfUnits.hh"
#include "G4Threading.hh"
#include "G4UnitsTable.hh"
#include "G4VScoringMesh.hh"
#include "MyConstant.hh"
#include "MyMacros.hh"
#include "PSSDoseAveragedLET.hh"
#include "PrimaryGeneratorAction.hh"
#include "RunData.hh"

// #include <iomanip>
// #include "DetectorConstruction.hh"
// #include "Randomize.hh"

RunAction::RunAction()
{
    MYG4TID(tid);
    DINFOLN("[tid {} RunAction] RunAction()", tid);
    G4RunManager::GetRunManager()->SetPrintProgress(5000);
}

void RunAction::BeginOfRunAction(const G4Run*)
{
    MYG4TID(tid);
    DINFOLN("[tid {} RunAction] BeginOfRunAction()", tid);
    _seed0 = std::chrono::high_resolution_clock::now();  // tik tok

#if 0
    // 每次 run 前清空 mesh
    G4ScoringManager* const scrMgr = G4ScoringManager::GetScoringManager();
    G4VScoringMesh* const mesh = scrMgr->FindMesh(_myScoreMeshName);
    scrMgr->CloseCurrentMesh();
    //if (mesh) mesh->Clear();
#endif

    ///============================================ 每轮修改粒子能量
    if (tid >= 0)
    {
        const auto engy = RunData::Instance().GetEnergy();
        LINFO("```````````````````````````````````````````````[tid {} RunAction/BeginOfRunAction] Setting particle energy = {}", tid, engy);
        _primGen->ReInitialize();
        _primGen->SetBeamEnergy(engy);
    }

    //============================================ 获取 socre mesh, 设置 primitive scorer
    // if (tid >= 0)
    // if (tid < 0)
    {
        auto* const scrMgr = G4ScoringManager::GetScoringManager();
        auto* const boxMesh = scrMgr->FindMesh(_myScoreMeshName);
        if (!boxMesh)
        {
            // DINFOLN("[tid {} RunAction]!! null box score mesh", tid);
            throw std::runtime_error(MYFMT("[tid {} RunAction/BeginOfRunAction] BeginOfRunAction, null box score mesh", tid));
        }
        //------------------- energy deposit scorer
        DINFOLN("[tid {} BeginOfRunAction/BeginOfRunAction] Setting score qtys", tid);
        G4PSEnergyDeposit* eDepScore = new G4PSEnergyDeposit(_myEngyDep, 0);
        /// mesh set score, mesh/fMFD: MultiFunctionalDetector->RegisterPrimitive(prs);
        // 随后, prs->SetMultiFunctionalDetector(this); 从而 score 中会包含 MFD 的信息
        boxMesh->SetPrimitiveScorer(eDepScore);

        //------------------- LETd scorer
        auto* const letdScore = new PSSDoseAveragedLET(_myLETd);
        boxMesh->SetPrimitiveScorer(letdScore);
    }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

void RunAction::EndOfRunAction(const G4Run* g4Run)
{
    MYG4TID(tid);
    // G4cout << "[RunAction End] End of run action\n";
    //================================== calculation time
    _seed1 = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double, std::milli> elapsed = _seed1 - _seed0;
    LINFO("[tid {:>4d} EndOfRunAction] Cost {:>13.4F} ms", tid, elapsed.count());
    //G4cout << "[RunAction End] Calculation time           = " << G4BestUnit(difftime(_seed1, _seed0), "Time") << " \n" << G4endl;

    //================================ 把结果写到当前能量目录
    auto* const scrMgr = G4ScoringManager::GetScoringManager();
#if 0
    auto* mesh = scrMgr->FindMesh(_myScoreMeshName);
    if (!mesh)
    {
        G4cout << "[RunAction End]!! null score mesh\n" << std::flush;
        return;
    }
#endif

#if 0
    // 取当前能量列表
    const auto& engys = RunData::Instance().GetEnergyList();
    if (engys.empty())
    {
        G4cout << "[RunAction End]!! empty eneries\n" << std::flush;
    }
#endif

    G4double gunEngy = RunData::Instance().GetEnergy();

    //=================================== dump eDep
    G4String edepFile = MYFMT("eDep_{}MeV.txt", (int)(gunEngy / MeV));
    scrMgr->DumpQuantityToFile(_myScoreMeshName, _myEngyDep, edepFile);

    // dump LETd
    G4String letFile = MYFMT("LETd_{}MeV.txt", (int)(gunEngy / MeV));
    scrMgr->DumpQuantityToFile(_myScoreMeshName, "LETd", letFile);
}
